#include <memory>
#include <timer.h>
#include <taskManager.h>
#include <utils.h>

Timer::Timer(const char* objTimerName, unsigned int intervalMs, Callback callback)
        : m_intervalMs(intervalMs), m_callback(std::move(callback)),
        m_needReset(false), m_abort(false), m_timerName(objTimerName)
    {
    }

Timer::~Timer() { stop(); }

LOCAL void timerTaskRun(Timer* pObj, TaskPtr task)
{
    pObj->run(task);
}

void Timer::start() 
{
    if (!isRunning()) 
    {
        CREATE_OBJTASK(utils::format("Timer_%s", m_timerName.c_str()).c_str(), reinterpret_cast<FUNCPTR>(timerTaskRun), this);
    }
}

void Timer::stop()
{
    if (isRunning()) 
    {
        std::lock_guard<std::mutex> lock(m_mutex);
        m_abort = true;
        m_cv.notify_one();
    }
}

void Timer::reset() 
{
    if (isRunning()) 
    {
        std::lock_guard<std::mutex> lock(m_mutex);
        m_needReset = true;
        m_cv.notify_one();
    }
}

bool Timer::isRunning() const 
{
    if (m_taskThread != nullptr)
    {
        return m_taskThread->isRunning();
    }
    return false;
}

void Timer::run(TaskPtr task)
{
    m_taskThread = task;
    while (true) 
    {
        std::unique_lock<std::mutex> lock(m_mutex);
        auto status = m_cv.wait_for(lock, std::chrono::milliseconds(m_intervalMs),
            [this]() { return m_abort || m_needReset; });

        if (m_needReset) 
        {
            m_needReset = false;
            continue;
        }

        if (m_abort) break;
            
        LOG_TRACE("Timer trigger: %s period %u ms", m_timerName.c_str(), m_intervalMs);
        if (!status && m_callback)
        {
            try { m_callback(); }
            catch (...) {}
        }
    }
}

int Timer::getInterval()
{
    return m_intervalMs;
}

std::string& Timer::getName()
{
    return  m_timerName;
}
